﻿using Styx;
using Styx.Pathing;
using Styx.WoWInternals.WoWObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace KittyDru.Helpers
{
    public static class StopMoving
    {
        public static bool InMoveToMeleeStopRange(WoWUnit unit)
        {
            if (unit == null || !unit.IsValid)
                return false;

            if (unit.IsPlayer)
                return unit.DistanceSqr < (2 * 2);

            float preferredDistance =  global::KittyDru.Unit.MeleeDistance(unit) - (unit.IsMoving ? 1.5f : 1f);
            if (unit.Distance <= preferredDistance && unit.IsWithinMeleeRange)
                return true;

            return false;
        }

        internal static StopType Type { get; set; }
        internal static WoWPoint Point { get; set; }
        internal static WoWUnit Unit { get; set; }
        internal static double Range { get; set; }
        internal static SimpleBooleanDelegate StopRequestDelegate { get; set; }
        public delegate bool SimpleBooleanDelegate(object context);

        internal static string callerName;
        internal static string callerFile;
        internal static int callerLine;

        public enum StopType
        {
            None = 0,
            Now,
            AsSoonAsPossible,
            Location,
            RangeOfLocation,
            RangeOfUnit,
            MeleeRangeOfUnit,
            LosOfUnit
        }

        private static LocalPlayer Me { get { return StyxWoW.Me; } }

        static StopMoving()
        {
            Clear();
        }

        public static void Clear()
        {
            Set(StopType.None, null, WoWPoint.Empty, 0, stop => false, null);
        }

        public static void Pulse()
        {
            if (Type == StopType.None)
                return;

            bool stopMovingNow;
            try
            {
                stopMovingNow = StopRequestDelegate(null);
            }
            catch
            {
                stopMovingNow = true;
            }

            if (stopMovingNow)
            {
                if (Me.IsMoving) Navigator.PlayerMover.MoveStop();

                Clear();
            }
        }

        private static void Set(StopType type, WoWUnit unit, WoWPoint pt, double range, SimpleBooleanDelegate stop, SimpleBooleanDelegate and)
        {
            Type = type;
            Unit = unit;
            Point = pt;
            Range = range;

            if (and == null)
                and = ret => true;

            StopRequestDelegate = ret => stop(ret) && and(ret);
        }

        public static void AtLocation(WoWPoint pt, SimpleBooleanDelegate and = null)
        {
            Set(StopType.Location, null, pt, 0, at => Me.Location.Distance(pt) <= 1, and);
        }

        public static void InRangeOfLocation(WoWPoint pt, double range, SimpleBooleanDelegate and = null)
        {
            Set(StopType.RangeOfLocation, null, pt, range, at => Me.Location.Distance(pt) <= range, and);
        }

        public static void InRangeOfUnit(WoWUnit unit, double range, SimpleBooleanDelegate and = null)
        {
            Set(StopType.RangeOfUnit, unit, WoWPoint.Empty, range, at => Unit == null || !Unit.IsValid || (Unit.SpellDistance() <= range && Unit.InLineOfSpellSight), and);
        }

        public static void InMeleeRangeOfUnit(WoWUnit unit, SimpleBooleanDelegate and = null)
        {
            Set(StopType.RangeOfUnit, unit, WoWPoint.Empty, 0, at => Unit == null || !Unit.IsValid || InMoveToMeleeStopRange(Unit), and);
        }

        public static void InLosOfUnit(WoWUnit unit, SimpleBooleanDelegate and = null)
        {
            Set(StopType.LosOfUnit, unit, WoWPoint.Empty, 0, at => Unit == null || !Unit.IsValid || Unit.InLineOfSpellSight, and);
        }

        public static void Now()
        {
            Set(StopType.Now, null, WoWPoint.Empty, 0, at => true, null);
            Pulse();
        }

        public static void AsSoonAsPossible(SimpleBooleanDelegate and = null)
        {
            Set(StopType.AsSoonAsPossible, null, WoWPoint.Empty, 0, at => true, and);
        }
    }
}
